package cn.caplike.demo.repository.security;

import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.Base64;
import java.util.Collections;
import java.util.Enumeration;

/**
 * {@link KeyStore} Helper<br>
 * - 如果 KeyStore 类型是 PKCS#12, 是不支持单位为 keypair 指定独立密钥的. 此时, keyStore 的密钥就是 keypair 的密钥
 *
 * @author LiKe
 * @version 1.0.0
 * @date 2020-07-11 10:03
 */
@Slf4j
public final class KeyStoreHelper {

    /**
     * 获取 KeyStore 的实例
     *
     * @return {@link KeyStore}
     */
    public static KeyStore create() throws KeyStoreException {
        // defaultType: jks
        return KeyStore.getInstance(KeyStore.getDefaultType());
    }

    /**
     * 向 KeyStore 中设值 Key 记录
     *
     * @param keyStore {@link KeyStore}
     */
    public static KeyStore setKeyEntry(KeyStore keyStore) throws CertificateException, NoSuchAlgorithmException, IOException, KeyStoreException {
        // ~ KeyStore 使用前必须加载, 加载一个空的 KeyStore
        keyStore.load(null, "My First Key Store's Password".toCharArray());

        // ~ 生成公私钥
        final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(512);
        final KeyPair keyPair = keyPairGenerator.generateKeyPair();
        final PrivateKey privateKey = keyPair.getPrivate();
        System.out.println("Generated PrivateKey: ");
        System.out.println(Base64.getEncoder().encodeToString(privateKey.getEncoded()));

        final PublicKey publicKey = keyPair.getPublic();

        // ~ 把指定的 Key 与指定的别名绑定, 并用指定的密码保护它
        keyStore.setKeyEntry(
            // 别名
            "My First Key Entry",
            privateKey,
            "My First Key Entry's Password".toCharArray(),
            Collections.singletonList(certificate(publicKey)).toArray(new Certificate[1])
        );

        return keyStore;
    }

    // keytool -changealias -keystore demo.jks -alias demo -destalias chaijia
    // 输入密钥库口令:
    public static void main(String[] args) throws Exception {
//        final KeyStore keyStore = setKeyEntry(create());
//
//        final KeyStore.ProtectionParameter protectionParameter = new KeyStore.PasswordProtection("My First Key Entry's Password".toCharArray());
//        final KeyStore.Entry keyEntry = keyStore.getEntry("My First Key Entry", protectionParameter);
//
//        final KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyEntry;
//        final PrivateKey privateKey = privateKeyEntry.getPrivateKey();
//
//        System.out.println("PrivateKey got from keyStore: ");
//        System.out.println(Base64.getEncoder().encodeToString(privateKey.getEncoded()));

        // read();
//        final KeyStore keyStore = create();
//        keyStore.set;
        // getKey("C:\\Users\\caplike\\Downloads\\signApk\\selfTerminal", "123123333", "chaibao", "123123333");
        log.info("-----------------------------------");
        getKey("C:\\Users\\caplike\\Downloads\\demo.jks", "xiangaix", "demo", "xiangaix");
    }

    /**
     * Description: 生成自签名的证书
     *
     * @param publicKey 公钥
     * @return java.security.cert.Certificate
     * @author LiKe
     * @date 2020-07-14 11:39:09
     */
    private static Certificate certificate(PublicKey publicKey) {
        return new Certificate("Self.Signed") {
            @Override
            public byte[] getEncoded() {
                return publicKey.getEncoded();
            }

            @Override
            public void verify(PublicKey key) {
                // do nothing
            }

            @Override
            public void verify(PublicKey key, String sigProvider) {
                // do nothing
            }

            @Override
            public String toString() {
                return Base64.getEncoder().encodeToString(getEncoded());
            }

            @Override
            public PublicKey getPublicKey() {
                return publicKey;
            }
        };
    }

    /**
     * Description: 生成证书 (X.509)
     *
     * @return java.security.cert.Certificate
     * @author LiKe
     * @date 2020-07-13 15:55:15
     */
    private static Certificate certificate() throws CertificateException {
        final CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");

        final String encodedPublicKey =
                "-----BEGIN CERTIFICATE-----\n" +
                        "MIICxDCCAi0CBECcV/wwDQYJKoZIhvcNAQEEBQAwgagxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIEwVU\n" +
                        "ZXhhczEPMA0GA1UEBxMGQXVzdGluMSowKAYDVQQKEyFUaGUgVW5pdmVyc2l0eSBvZiBUZXhhcyBh\n" +
                        "dCBBdXN0aW4xKDAmBgNVBAsTH0luZm9ybWF0aW9uIFRlY2hub2xvZ3kgU2VydmljZXMxIjAgBgNV\n" +
                        "BAMTGXhtbGdhdGV3YXkuaXRzLnV0ZXhhcy5lZHUwHhcNMDQwNTA4MDM0NjA0WhcNMDQwODA2MDM0\n" +
                        "NjA0WjCBqDELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMQ8wDQYDVQQHEwZBdXN0aW4xKjAo\n" +
                        "BgNVBAoTIVRoZSBVbml2ZXJzaXR5IG9mIFRleGFzIGF0IEF1c3RpbjEoMCYGA1UECxMfSW5mb3Jt\n" +
                        "YXRpb24gVGVjaG5vbG9neSBTZXJ2aWNlczEiMCAGA1UEAxMZeG1sZ2F0ZXdheS5pdHMudXRleGFz\n" +
                        "LmVkdTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsmc+6+NjLmanvh+FvBziYdBwTiz+d/DZ\n" +
                        "Uy2jyvij6f8Xly6zkhHLSsuBzw08wPzr2K+F359bf9T3uiZMuao//FBGtDrTYpvQwkn4PFZwSeY2\n" +
                        "Ynw4edxp1JEWT2zfOY+QJDfNgpsYQ9hrHDwqnpbMVVqjdBq5RgTKGhFBj9kxEq0CAwEAATANBgkq\n" +
                        "hkiG9w0BAQQFAAOBgQCPYGXF6oRbnjti3CPtjfwORoO7ab1QzNS9Z2rLMuPnt6POlm1A3UPEwCS8\n" +
                        "6flTlAqg19Sh47H7+Iq/LuzotKvUE5ugK52QRNMa4c0OSaO5UEM5EfVox1pT9tZV1Z3whYYMhThg\n" +
                        "oC4y/On0NUVMN5xfF/GpSACga/bVjoNvd8HWEg==\n" +
                        "-----END CERTIFICATE-----";

        return certificateFactory.generateCertificate(new ByteArrayInputStream(encodedPublicKey.getBytes()));
    }

    // -----------------------------------------------------------------------------------------------------------------

    /**
     * Description: 从证书中读取公钥和私钥.
     *
     * @return void
     * @author LiKe
     * @date 2020-07-14 15:49:15
     */
    public static void read() throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException {
        final KeyStore keyStore = KeyStore.getInstance("JKS");
        keyStore.load(
            new FileInputStream(
                "D:\\caplike\\coding\\workspace\\java\\demo\\demo-spring-security\\demo-spring-security-oauth2\\token-customize-authorization-server\\src\\main\\resources\\authorization-server.jks"),
            "SCLiKe11040218".toCharArray()
        );

        final PublicKey publicKey = keyStore.getCertificate("authorization-server-jwt-keypair").getPublicKey();
        System.out.println("PublicKey: ");
        System.out.println(Base64.getEncoder().encodeToString(publicKey.getEncoded()));

        final Key key = keyStore.getKey("authorization-server-jwt-keypair", "GFLiKe0218".toCharArray());
        System.out.println("PrivateKey: ");
        System.out.println(Base64.getEncoder().encodeToString(key.getEncoded()));
    }

    /**
     * Description: 从证书中读取公钥和私钥.
     *
     * @return void
     * @author LiKe
     * @date 2022-03-29 09:22:07
     */
    public static Key getKey(final String keyStoreLocation, final String password, final String keyAlias, final String keyPassword)
        throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException {
        final KeyStore keyStore = KeyStore.getInstance("JKS");
        try (final FileInputStream keyStoreInputStream = new FileInputStream(keyStoreLocation)) {
            keyStore.load(keyStoreInputStream, password.toCharArray());

            final Enumeration<String> aliases = keyStore.aliases();
            while (aliases.hasMoreElements()) {
                final String alias = aliases.nextElement();
                final Certificate certificate = keyStore.getCertificate(alias);
                // keyStore.getKey(alias, )
                log.info("alias: {} - {}", alias, /*new String(certificate.getPublicKey().getEncoded())*//*certificate.getPublicKey().toString()*//*new String(certificate.getEncoded())*/"");
            }
            final Key key = keyStore.getKey(keyAlias, keyPassword.toCharArray());
            return key;
        }
    }


}
